package org.canaan.http.core;

import org.apache.http.client.CookieStore;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.canaan.http.HttpClient;
import org.canaan.http.HttpResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.Map;

/**
 * @author Canaan
 * @date 2017/7/17
 */
public abstract class AbstractHttpClient<P> implements HttpClient<P> {
    private final static Logger LOGGER = LoggerFactory.getLogger(AbstractHttpClient.class);
    private static final PoolingHttpClientConnectionManager CLIENT_MANAGER;
    private static final CloseableHttpClient                CLIENT;

    static {
        CLIENT_MANAGER =  new PoolingHttpClientConnectionManager();
        CLIENT_MANAGER.setMaxTotal(1500);        //最大连接数
        CLIENT_MANAGER.setDefaultMaxPerRoute(450); //每个路由上的默认连接个数
        CLIENT = HttpClients.custom().setConnectionManager(CLIENT_MANAGER).build();
    }

    protected final Map<String, String>   header; //请求头
    protected final Map<String, Object>   cookies;     //设置cookies
    protected final RequestConfig         requestConfig; //请求参数设置
    private final   Integer               repeatTimes;      //重复执行次数
    private final   boolean               autoClose;    //是否自动关闭
    protected       CloseableHttpClient   httpClient;
    protected       CookieStore           cookieStore;
    protected       CloseableHttpResponse response;

    protected AbstractHttpClient(AbstractHttpBuilder builder) {
        this.header = builder.header;
        this.cookies = builder.cookies;
        this.repeatTimes = builder.repeatTimes <= 0 ? 1 : builder.repeatTimes;
        this.requestConfig = builder.requestConfig;
        this.autoClose = builder.autoClose;
        if (builder.hasCookies) {
            cookieStore = new BasicCookieStore();
            httpClient = HttpClients.custom().setDefaultCookieStore(cookieStore).build();
            return;
        }
        if (builder.isSingle) {
            httpClient = CLIENT;
            return;
        }
        httpClient = HttpClients.custom().build();
    }

    /**
     执行http请求
     @param url 请求路径
     @param param 请求参数

     @return
     @throws IOException
     */
    protected abstract HttpResponse execute(String url, P param) throws IOException;

    /**
     * 带重复执行的http请求
     *
     * @param url       请求路径
     * @param param     请求参数
     * @param condition 重复条件
     * @return
     * @throws IOException
     */
    public HttpResponse execute(String url, P param, HttpRepeatCondition condition) throws IOException {
        try {

            if (condition == null) {
                return this.execute(url, param);
            }

            HttpResponse response = null;
            IOException ex = null;
            int _repeatTimes = this.repeatTimes;

            do {
                if (_repeatTimes != this.repeatTimes) {
                    LOGGER.info("http请求重复执行 {} 次，url: {} \n model: {} ", this.repeatTimes - _repeatTimes, url, param, ex);
                }
                _repeatTimes--;
                try {
                    response = this.execute(url, param);
                    ex = null;
                } catch (IOException e) {
                    ex = e;
                }
            } while (condition.doRepeat(response, ex) && _repeatTimes > 0);

            if (ex != null) {
                throw ex;
            }
            return response;

        } finally {
            this.closeResponse();
            if (autoClose) {
                this.closeClient();
            }
        }
    }

    /*
     关闭client
     */
    private void closeClient() {
        try {
            if (httpClient != null && this.httpClient.equals(CLIENT)) {
                //单例的client不能关闭，不然下次就不能用了
                return;
            }
            if (httpClient != null) {
                httpClient.close();
                httpClient = null;
            }
        } catch (IOException e) {
            LOGGER.error("httpClient资源关闭失败", e);
        }
    }

    /**
     关闭response
     */
    private void closeResponse() {
        try {
            if (response != null) {
                response.close();
                response = null;
            }
        } catch (IOException e) {
            LOGGER.error("httpResponse资源关闭失败", e);
        }
    }


    /**
     关闭资源
     */
    @Override
    public void close() {
        this.closeClient();
        this.closeResponse();
    }

}
